package org.aldar.bquest.logic.controller.board;

import org.aldar.bquest.constants.GameCycleState;
import org.aldar.bquest.constants.IConstants;
import org.aldar.bquest.constants.LandscapeType;
import org.aldar.bquest.constants.TileState;
import org.aldar.bquest.effects.SymbolAnimationUtil;
import org.aldar.bquest.entity.ManaIncreasingTile;
import org.aldar.bquest.entity.TileSprite;
import org.aldar.bquest.entity.TileSpriteUtil;
import org.aldar.bquest.entity.effect.ParticleUtil;
import org.aldar.bquest.entity.pool.TilesPool;
import org.aldar.bquest.logic.TilesGameCycle;
import org.aldar.bquest.logic.controller.board.transform.TileBoardTransform;
import org.aldar.bquest.logic.controller.entity.HeroAnimationController;
import org.aldar.bquest.logic.controller.entity.TileBoard;
import org.aldar.bquest.logic.controller.level.LevelController;
import org.aldar.bquest.logic.controller.player.ManaAndPointsController;
import org.aldar.bquest.profile.GameProfile;
import org.aldar.bquest.resources.ResourceManager;
import org.aldar.bquest.screens.MainBoardScreen;
import org.aldar.bquest.sound.GameSound;
import org.aldar.bquest.util.Logger;
import org.aldar.bquest.util.SpriteUtil;
import org.andengine.engine.handler.timer.ITimerCallback;
import org.andengine.engine.handler.timer.TimerHandler;
import org.andengine.entity.Entity;
import org.andengine.entity.IEntity;
import org.andengine.entity.modifier.*;
import org.andengine.entity.sprite.Sprite;
import org.andengine.util.math.MathUtils;
import org.andengine.util.modifier.IModifier;

import java.util.*;

/**
 * User: Daria
 * Date: 5/31/13 6:43 PM
 */
public class AfterMatchProcessor implements IConstants {

    public static Set<String> killedKeys = new HashSet<String>();

    public static Set<TileSprite> foundMatches = new HashSet<TileSprite>();

    public static int tilesToRefill = 0;

    public static boolean firstMatchWasCrystal = false;

    public static int matchSequence = 0;

    private static Map<LandscapeType, List<TileSprite>> matchCountByStyle = new HashMap<LandscapeType, List<TileSprite>>();
    private static final Logger LOG = new Logger(AfterMatchProcessor.class);
    public static boolean firstMatchWasCurrentLandscape;

    public static void resetState() {
        matchSequence = 0;
        firstMatchWasCrystal = false;
        tilesToRefill = 0;
        foundMatches = new HashSet<TileSprite>();
        killedKeys = new HashSet<String>();
        matchCountByStyle = new HashMap<LandscapeType, List<TileSprite>>();
    }

    public static void dissolveMatchedTilesAndCountBonus() {
        boolean matchContainsCrystal = false;
        boolean matchContainsWaterOrLava = false;
        int totalMatchedStyles = 0;
        if (matchCountByStyle.size() > 0) {
            boolean currentLsFound = false;
            for (LandscapeType style : LandscapeType.values()) {  //counting bonus for each LS style
                //LOG.i("Checking style on match: " + style.name());
                List<TileSprite> matchesForStyle = matchCountByStyle.get(style);
                Integer styleMatchCount = (matchesForStyle == null ? 0 : matchesForStyle.size());
                if (styleMatchCount != null && styleMatchCount >= 3 && !LevelController.getTimeLine().isLevelFinished()) {
                    totalMatchedStyles++;
                    matchContainsWaterOrLava =
                            matchContainsWaterOrLava ||
                                    (style == LandscapeType.WATER ||
                                            style == LandscapeType.WATER_GREEN ||
                                            style == LandscapeType.LAVA);

                    if (styleMatchCount > 3) {
                        //create ability tile for any type of landscape match with >3 tiles!
                        LOG.i("MATCH 4 and more - adding ability tile!");
                        int row = matchesForStyle.get(0).getCurrentRow();
                        int col = matchesForStyle.get(0).getCurrentCol();
                        TileBoard.getCrystalTileKeys().add(TileBoard.getKey(row, col) + "_true"); //true means "ability tile"
                    }

                    GameProfile.getCurrentLevelProfile().addAchievedPoints(20 * (matchSequence + 1) * (styleMatchCount - 2), styleMatchCount - 2,
                            matchesForStyle.get(0).getTile().getX(),
                            matchesForStyle.get(0).getTile().getY());

                    LandscapeType startLandscape = LevelController.getTimeLine().getCurrentLandscape().getLandscapeType();
                    //LOG.i("Start landscape for match is: " + startLandscape.toString());
                    if (style == startLandscape && !LevelController.getTimeLine().isLevelFinished() && !currentLsFound &&
                            LevelController.getTimeLine().isCurrentLandscapeReached()) {
                        //set this so that only 1 matched style will move the hero forward!
                        currentLsFound = true;
                        //LOG.i("MATCH landscape match!!!");
                        //move once for first 3 tiles matched + 1 more move for each next matched tile!
                        if (matchSequence == 0) {
                            firstMatchWasCurrentLandscape = true;
                        }
                        if (!HeroAnimationController.getInstance().isHeroShooting() &&
                                !HeroAnimationController.getInstance().isHeroPlayingConcert()) {
                            int movementMatches = styleMatchCount - 2;
                            while (LevelController.getTimeLine().getCurrentLandscape() != null &&
                                    LevelController.getTimeLine().getCurrentLandscape().getLandscapeType() == style
                                    && movementMatches > 0) {
                                LevelController.getTimeLine().proceedLevelSteps(1);
                                SymbolAnimationUtil.moveSymbolUp(
                                        TileSpriteUtil.getTileCenterX(matchesForStyle.get(0).getTile().getX()),
                                        TileSpriteUtil.getTileCenterY(matchesForStyle.get(0).getTile().getY()), SpriteUtil.getMoveHeroSymbolSprite());
                                movementMatches--;
                            }
                        }
                    } else if (style == LandscapeType.CRYSTAL || style == LandscapeType.CRYSTAL_ABILITY || style == LandscapeType.CRYSTAL_BASE) {
                        matchContainsCrystal = true;
                        LOG.i("Crystal style match!");
                        if (matchSequence == 0) {
                            firstMatchWasCrystal = true;
                        }
                        //LOG.i("MATCH addming mana for crystal match!");
//moved to crystal sprite performAction
//                        ManaAndPointsController.getInstance().addBonus(styleMatchCount);
                    } else {
                        if (style.isProduceCrystal() && styleMatchCount == 3) {
                            LOG.i("MATCH simple match - adding crystal tile!");
                            int row = matchesForStyle.get(0).getCurrentRow();
                            int col = matchesForStyle.get(0).getCurrentCol();
                            TileBoard.getCrystalTileKeys().add(TileBoard.getKey(row, col) + "_false"); //false means "not ability tile"
                        } else {
                            //LOG.i("MATCH simple match - not adding crystal tile for style " + style.name());
                        }
                    }

                } else {
                    //LOG.i("no match for style");
                }
            }
            //FIXME rollback and improve countAvailableMoves method!!!!!
            /*
            int availableMoves = AvailableMovesCounter.checkAvailableMoves();
            LOG.i("1 available moves = " + availableMoves);
            if (availableMoves <= 0) {
                TileBoard.recreateHopelessTileBoard();
            } */
            if (matchSequence == 0 && !firstMatchWasCrystal) {
                ManaAndPointsController.getInstance().subtractMana(manaUseForMatch);
            }
            if (matchContainsCrystal) {
                MainBoardScreen.getInstance().getGameSound().playCrystalMatchSound(matchSequence);
            } else if (matchContainsWaterOrLava && totalMatchedStyles == 1) {
                MainBoardScreen.getInstance().getGameSound().playBubbleMatchSound(matchSequence);
            } else {
                MainBoardScreen.getInstance().getGameSound().playSimpleMatchSound(matchSequence);
            }
            matchSequence++;
        }
        matchCountByStyle.clear();
        if (LevelController.getTimeLine().isLevelFinished()) {
            TilesGameCycle.getInstance().setState(GameCycleState.WAITING);
        }
    }


    public static void fillEmpty() {
        LOG.i("FILL EMPTY CALLED!");
        int deltaRow, deltaCol;
        deltaRow = 0;
        deltaCol = 0;
        for (int i = 0; i < CELLS_VERTICAL; i++) {
            boolean matchStarted = false;
            int matchedTiles = 0;
            int col = -1;
            int row = -1;
            Integer[] lastValidCellArr = new Integer[CELLS_VERTICAL];
            Integer[] lastMatchedCellArr = new Integer[CELLS_VERTICAL];
            float[] lastDelayArr = new float[CELLS_VERTICAL];
            for (int j = 0; j < CELLS_HORIZONTAL; j++) {
                switch (TilesGameCycle.getLastMove()) {
                    case MOVE_RIGHT:
                        row = i;
                        col = CELLS_HORIZONTAL - j - 1;
                        deltaRow = 0;
                        deltaCol = 1;
                        break;
                    case MOVE_LEFT:
                        row = i;
                        col = j;
                        deltaRow = 0;
                        deltaCol = -1;
                        break;
                    case MOVE_UP:
                        row = j;
                        col = i;
                        deltaRow = -1;
                        deltaCol = 0;
                        break;
                    case MOVE_DOWN:
                        row = CELLS_HORIZONTAL - j - 1;
                        col = i;
                        deltaRow = 1;
                        deltaCol = 0;
                        break;
                    default:
                }
                if (TileBoard.isCellValid(col, row)) {
                    lastValidCellArr[i] = j;
                    final TileSprite tile = TileBoard.getTileMap().get(TileBoard.getKey(row, col));
                    if (tile.getState() == TileState.STATE_FALL_FINISH) {
                        //log("Fill Empty - matched tile found col row " + col + "  " + row);
                        tile.detachFromLayer();
                        TileSpriteUtil.releaseTile(tile);
                        lastMatchedCellArr[i] = j;
                        matchStarted = true;
                        matchedTiles++;
                    } else {
                        if (matchStarted) {
                            final TileSprite tileToMove = TileBoard.getTileMap().get(TileBoard.getKey(row, col));

                            int newCol = col + deltaCol * matchedTiles;
                            int newRow = row + deltaRow * matchedTiles;

                            final float[] screenXY = TileBoardTransform.getSpriteAdjustedScreenCoords(
                                    TileBoardTransform.getScreenCoords(
                                            newCol * CELL_WIDTH,
                                            newRow * CELL_HEIGHT));
                            tilesToRefill++;
                            LOG.i("increasing tiles to refill, new value " + tilesToRefill);
                            float delay = (j - lastMatchedCellArr[i]) / 50f;
                            lastDelayArr[i] = delay;
                            tileToMove.moveWithDelay(delay, screenXY, new IEntityModifier.IEntityModifierListener() {
                                @Override
                                public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                                }

                                @Override
                                public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                                    tile.setInMovement(false);
                                    tilesToRefill--;
                                    LOG.i("decreasing tiles to refill, new value " + tilesToRefill);
                                    if (tilesToRefill == 0 && LevelController.getCurrentLevelState() == LevelController.LevelState.PROGRESS) {
                                        LOG.i("tilesToRefill = 0, setting status to CHECK");
                                        //MainBoardScreen.getInstance().getGameSound().playTileRefillSound();
                                        checkAndStartEarthquake(new Runnable() {
                                            @Override
                                            public void run() {
                                                TilesGameCycle.getInstance().setState(GameCycleState.CHECK);
                                            }
                                        });
                                    } else {
                                        LOG.i("tiles to refill is still > 0! cannot set status to CHECK");
                                    }
                                    tileToMove.restartAnimation();
                                }
                            });

                            tileToMove.setCurrentCol(newCol);
                            tileToMove.setCurrentRow(newRow);
                            TileBoard.getTileMap().put(TileBoard.getKey(newRow, newCol), tileToMove);
                            tileToMove.swapLayer(MainBoardScreen.getScm().getJwLayer()[col][row], MainBoardScreen.getScm().getJwLayer()[newCol][newRow]);
                        }
                    }
                }
            }
            //move new MainBoardScreen!
            for (int j = 0; j < matchedTiles; j++) {
                if (lastValidCellArr[i] != null) {
                    switch (TilesGameCycle.getLastMove()) {
                        case MOVE_RIGHT:
                            row = i;
                            col = (CELLS_HORIZONTAL - lastValidCellArr[i]) - j - 2;
                            break;
                        case MOVE_LEFT:
                            row = i;
                            col = lastValidCellArr[i] + j + 1;
                            break;
                        case MOVE_UP:
                            row = lastValidCellArr[i] + j + 1;
                            col = i;
                            break;
                        case MOVE_DOWN:
                            row = (CELLS_HORIZONTAL - lastValidCellArr[i]) - j - 2;
                            col = i;
                            break;
                        default:
                    }
                    tilesToRefill++;
                    LOG.i("increasing tiles to refill for match tile, new value " + tilesToRefill);

                    float[] startXY = TileBoardTransform.getSpriteAdjustedScreenCoords(
                            TileBoardTransform.getScreenCoords(
                                    col * CELL_WIDTH,
                                    row * CELL_HEIGHT));

                    final int newCol = col + deltaCol * matchedTiles;
                    final int newRow = row + deltaRow * matchedTiles;

                    float delay = lastDelayArr[i] + (j + 1) / 30f;
                    final TileSprite newTile = TileSpriteUtil.getRandomTile(newRow, newCol, 1);
                    TileBoard.getTileMap().put(TileBoard.getKey(newRow, newCol), newTile);
                    MainBoardScreen.getInstanceEngine().runOnUpdateThread(new Runnable() {
                        @Override
                        public void run() {
                            newTile.attachToLayer(MainBoardScreen.getScm().getJwLayer()[newCol][newRow]);
                        }
                    });


                    float[] newXY = TileBoardTransform.getSpriteAdjustedScreenCoords(
                            TileBoardTransform.getScreenCoords(
                                    newCol * CELL_WIDTH,
                                    newRow * CELL_HEIGHT));
                    newTile.getTile().setX(startXY[0]);
                    newTile.getTile().setY(startXY[1]);
                    newTile.moveWithDelay(delay, newXY, new IEntityModifier.IEntityModifierListener() {
                        @Override
                        public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                        }

                        @Override
                        public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                            newTile.setInMovement(false);
                            tilesToRefill--;
                            LOG.i("decreasing tiles to refill for match tile, new value " + tilesToRefill);
                            if (tilesToRefill == 0 && LevelController.getCurrentLevelState() == LevelController.LevelState.PROGRESS) {
                                LOG.i("tilesToRefill = 0, setting status to CHECK");
                                checkAndStartEarthquake(new Runnable() {
                                    @Override
                                    public void run() {
                                        TilesGameCycle.getInstance().setState(GameCycleState.CHECK);
                                    }
                                });
                            } else {
                                {
                                    LOG.i("tiles to refill is still > 0! cannot set status to CHECK");
                                }
                            }
                        }
                    });
                }
            }
        }
        LOG.i("FILL EMPTY FINISHED");
    }

    public static void removeMatchedTiles() {
        long totalmillis1 = new Date().getTime();
        killedKeys.clear();
        Set<TileSprite> matchedTiles = new HashSet<TileSprite>();
        if (foundMatches == null || foundMatches.size() == 0) {
            long millis1 = new Date().getTime();

            //count matchCountByStyle only if they have not been counted yet!
            for (int row = 0; row < IConstants.CELLS_VERTICAL; row++) {
                for (int col = 0; col < IConstants.CELLS_HORIZONTAL; col++) {
                    matchedTiles.addAll(MatchedTilesFinder.getAllMatchedTilesFor(col, row));
                }
            }
            long millis2 = new Date().getTime();
            LOG.i("Processing matched 1 ms: " + (millis2 - millis1));


        } else {
            //if already counted - take the existing array
            matchedTiles = foundMatches;
        }
        foundMatches.clear();
        LOG.i("TOTAL MATCHED TILES: " + matchedTiles.size());
        boolean matchedSomeFire = false;
        long millis1 = new Date().getTime();

        boolean matchedFireOrEnemy = false;
        for (final TileSprite matchedTile : matchedTiles) {
            if (matchedTile.getEnemyImp() != null) {
                matchedFireOrEnemy = true;
                final Entity enemyImpCopy = matchedTile.getEnemyImp();

                /*new ParticleEmitter(15, 50, 1f,
                        TileSpriteUtil.getTileCenterX(matchedTile.getTile().getX()),
                        TileSpriteUtil.getTileCenterY(matchedTile.getTile().getY()),
                        ResourceManager.getInstance().getParticleRegion()).start();
                           ParticleUtil.makeParticleCircleForTileMatch(
                        TileSpriteUtil.getTileCenterX(matchedTile.getTile().getX()),
                        TileSpriteUtil.getTileCenterY(matchedTile.getTile().getY())
                );
*/
                //replaced with smoke effect
                ParticleUtil.effectDisappearWithSmoke(TileSpriteUtil.getTileCenterX(matchedTile.getTile().getX()),
                        TileSpriteUtil.getTileCenterY(matchedTile.getTile().getY()));
                GameProfile.getCurrentLevelProfile().addAchievedPoints(10, 1, matchedTile.getTile().getX(), matchedTile.getTile().getY());
                enemyImpCopy.detachSelf();
                matchedTile.setEnemyImp(null);

            } else if (matchedTile.getFireOnTile() != null) {
                matchedFireOrEnemy = true;
                matchedTile.getFireOnTile().detachSelf();
                matchedTile.setFireOnTile(null); //set null now to prevent starting enemy actions!!
/*                ParticleUtil.makeParticleCircleForTileMatch(
                        TileSpriteUtil.getTileCenterX(matchedTile.getTile().getX()),
                        TileSpriteUtil.getTileCenterY(matchedTile.getTile().getY())
                );
                */
                ParticleUtil.effectExtinguishFire(TileSpriteUtil.getTileCenterX(matchedTile.getTile().getX()),
                        TileSpriteUtil.getTileCenterY(matchedTile.getTile().getY()));

                GameProfile.getCurrentLevelProfile().addAchievedPoints(10, 1, matchedTile.getTile().getX(), matchedTile.getTile().getY());
            } else {
                //TileBoard.addCrystalTileKey(TileBoard.getKey(matchedTile.getCurrentRow(), matchedTile.getCurrentCol()));

                matchedTile.setState(TileState.STATE_KILLED);
                matchedTile.performMatchAction(matchedTile.getCurrentCol(), matchedTile.getCurrentRow());
                if (matchedTile.producesMana() && matchedTile instanceof ManaIncreasingTile) {
                    ManaAndPointsController.getInstance().addPartialMana(
                            //GameProfile.getCurrentLevel().getManaSettings().getAmountAddForCrystal()
                            ((ManaIncreasingTile) matchedTile).getManaAmountForMatch()
                    );
                }
                List<TileSprite> prevItems = null;
                if (matchedTile.getStyle().getBaseType() != null) {
                    prevItems = matchCountByStyle.get(matchedTile.getStyle().getBaseType());
                } else {
                    prevItems = matchCountByStyle.get(matchedTile.getStyle());
                }

                if (prevItems == null) {
                    prevItems = new LinkedList<TileSprite>();
                }
                prevItems.add(matchedTile);
                if (matchedTile.getStyle().getBaseType() != null) {
                    //count small and ability crystals together for 4x bonus
                    matchCountByStyle.put(matchedTile.getStyle().getBaseType(), prevItems);
                } else {
                    matchCountByStyle.put(matchedTile.getStyle(), prevItems);

                }
                killedKeys.add(TileBoard.getKey(matchedTile.getCurrentRow(), matchedTile.getCurrentCol()));
            }
        }

        long millis2 = new Date().getTime();
        LOG.i("Processing matched 2 ms: " + (millis2 - millis1));

        ManaAndPointsController.getInstance().fireCollectedManaSymbol();

        LOG.i("killed keys = " + killedKeys.size());
        if (killedKeys.size() == 0 /*&& !matchedSomeFire*/) {
            if (!HeroAnimationController.getInstance().isWalking()) {
                millis1 = new Date().getTime();
                if (TouchAndMoveController.lastMoveWasMatch && !matchedFireOrEnemy) {
                    //LOG.i()

                    if (!firstMatchWasCrystal/* && !firstMatchWasCurrentLandscape*/) { //manual crystal match should not decrease mana!
                        GameProfile.getCurrentLevelProfile().addTurn();
                        //NEW subtract Mana for match action!!!
                        float manaEmpty = ManaAndPointsController.getInstance().getCurrentManaAmount();
                        if (manaEmpty <= 0 && LevelController.getCurrentLevelState() != LevelController.LevelState.FINISHED) {
                            LOG.i("OOPS After Match Mana is EMPTY - Game Over Hahaha...");
                            LevelController.getInstance().setCurrentLevelState(LevelController.LevelState.FAILED);
                            //calling onLevelFailed() moved to TilesGameCycle onResetWaitingSwap for game smoothness
                        } else {
                            LOG.i("After Match Mana is not empty - remaining " + manaEmpty);
                        }
                    }

                    performEnemyActions(new Runnable() {
                        @Override
                        public void run() {
                            TilesGameCycle.getInstance().resetToWaitingSwap();
                        }

                    });
                    //lets try... not wait until enemy actions are finished
                    //TilesGameCycle.getInstance().resetToWaitingSwap();
                } else {
                    if (matchedFireOrEnemy) {
                        TilesGameCycle.getInstance().setState(GameCycleState.CHECK);
                    } else {
                        TilesGameCycle.getInstance().resetToWaitingSwap();
                    }
                }
                millis2 = new Date().getTime();
                LOG.i("Part 1 ms: " + (millis2 - millis1));

            } else {
                millis1 = new Date().getTime();

                if (TouchAndMoveController.lastMoveWasMatch && !matchedFireOrEnemy) {
                    if (!firstMatchWasCrystal/* && !firstMatchWasCurrentLandscape*/) { //manual crystal match should not decrease mana!
                        GameProfile.getCurrentLevelProfile().addTurn();
                        //NEW subtract Mana for match action!!!
                        float manaEmpty = ManaAndPointsController.getInstance().getCurrentManaAmount();
                        if (manaEmpty <= 0 && LevelController.getCurrentLevelState() != LevelController.LevelState.FINISHED) {
                            LOG.i("OOPS After Match Mana is EMPTY - Game Over Hahaha...");
                            LevelController.getInstance().setCurrentLevelState(LevelController.LevelState.FAILED);
                            //calling onLevelFailed() moved to TilesGameCycle onResetWaitingSwap for game smoothness
                        } else {
                            LOG.i("After Match Mana is not empty - remaining " + manaEmpty);
                        }
                    }

                    performEnemyActions(new Runnable() {
                        @Override
                        public void run() {
                            TilesGameCycle.getInstance().resetToWaitingSwap();
                        }
                    });
                } else {
                    if (matchedFireOrEnemy) {
                        TilesGameCycle.getInstance().setState(GameCycleState.CHECK);

                    } else {
                        TilesGameCycle.getInstance().resetToWaitingSwap();
                    }
                }
                millis2 = new Date().getTime();
                LOG.i("Part2 ms: " + (millis2 - millis1));

            }
        } else {
            long millis5 = new Date().getTime();
            dissolveMatchedTilesAndCountBonus();
            long millis6 = new Date().getTime();
            LOG.i("Count bonus ms: " + (millis6 - millis5));

        }
        long totalmillis2 = new Date().getTime();
        LOG.i("Total removeMatched ms: " + (totalmillis2 - totalmillis1));


    }


    private static void performEnemyActions(final Runnable runnable) {
        LOG.i("performing enemy actions");
        final int[] enemyImpsInAction = {0};
        for (int row = 0; row < IConstants.CELLS_VERTICAL; row++) {
            for (int col = 0; col < IConstants.CELLS_HORIZONTAL; col++) {
                if (TileBoard.isCellValid(col, row)) {
                    final TileSprite tile = TileBoard.getTileMap().get(TileBoard.getKey(row, col));
                    if (tile.getEnemyImp() != null && !tile.isEnemyImpInAction()) {
                        LOG.i("found enemy imp on tile " + col + " " + row);
                        List<TileSprite> adjTargets = getAdjacentTilesToFire(col, row);
                        LOG.i("number of possible target tiles to set on fire: " + adjTargets.size());
                        if (adjTargets.size() > 0) {
                            int index = new Random().nextInt(adjTargets.size());
                            final TileSprite targetTile = adjTargets.get(index);
                            LOG.i("chosen target fire tile " + targetTile.getCurrentCol() + " " + targetTile.getCurrentRow());
                            targetTile.enableFireOnTile(tile.getTile().getX(), tile.getTile().getY());
                            final Sprite enemyImp = tile.getEnemyImp();
                            ParticleUtil.effectFlames(0.5f, TileSpriteUtil.getTileCenterX(tile.getTile().getX()),
                                    TileSpriteUtil.getTileCenterY(tile.getTile().getY()));
                            tile.setEnemyImpInAction(false);
                            if (enemyImpsInAction[0] == 0) {
                                runnable.run();
                            }

                        }
                    }
                }
            }
        }
        //run after actions

        if (enemyImpsInAction[0] == 0) {
            runnable.run();
        }
    }

    private static List<TileSprite> getAdjacentTilesToFire(int col, int row) {
        List<TileSprite> result = new LinkedList<TileSprite>();
        for (int i = -1; i < 2; i++) {
            for (int j = -1; j < 2; j++) {
                if (j != col || j != row) { //middle tile has to be excluded!
                    if (TileBoard.isCellValid(col + i, row + j)) {
                        TileSprite tile = TileBoard.getTileMap().get(TileBoard.getKey(row + j, col + i));
                        if (isTileReadyToFire(tile)) {
                            LOG.i("found target fire tile, key= " + TileBoard.getKey(row + j, col + i) + " current = " + tile.getCurrentCol() + " " + tile.getCurrentRow());
                            result.add(tile);
                        }
                    }
                }
            }
        }
        return result;
    }

    private static boolean isTileReadyToFire(TileSprite tile) {
        if (tile.getEnemyImp() != null) {
            return false;
        }
        if (tile.getFireOnTile() != null) {
            return false;
        }
        if (tile.getStyle().getBaseType() == LandscapeType.CRYSTAL_BASE) {
            return false;
        }
        if (tile.getStyle() == LandscapeType.WATER) {
            return false;
        }
        if (tile.getStyle() == LandscapeType.WATER_GREEN) {
            return false;
        }
        return true;

    }

    public static void checkAndStartEarthquake(final Runnable onFinish) {
        if (AfterMatchProcessor.matchSequence != 3) {
            onFinish.run();
            return;
        }
        final Sprite earthquakeText = TilesPool.getEntityForTexture(ResourceManager.getInstance().getSymbolEarthquake());
        earthquakeText.setPosition(CAMERA_WIDTH / 2, CAMERA_HEIGHT / 2);
        MainBoardScreen.getScm().getTutorialLayer().attachChild(earthquakeText);
        earthquakeText.registerEntityModifier(new SequenceEntityModifier(
                new ParallelEntityModifier(
                        new AlphaModifier(0.3f, 0, 1, new IEntityModifier.IEntityModifierListener() {
                            @Override
                            public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                            }

                            @Override
                            public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                                //To change body of implemented methods use File | Settings | File Templates.
                            }
                        }),
                        new ScaleModifier(0.3f, 0.7f, 1f, new IEntityModifier.IEntityModifierListener() {
                            @Override
                            public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                            }

                            @Override
                            public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                                startEarthquake(onFinish);
                            }
                        })),
                new ParallelEntityModifier(
                        new SequenceEntityModifier(
                                new MoveByModifier(0.1f, 10, 0),
                                new MoveByModifier(0.1f, -10, 0),
                                new MoveByModifier(0.1f, 10, 0),
                                new MoveByModifier(0.1f, -10, 0),
                                new MoveByModifier(0.1f, 10, 0),
                                new MoveByModifier(0.1f, -10, 0),
                                new MoveByModifier(0.1f, 10, 0),
                                new MoveByModifier(0.1f, -10, 0)
                        ),
                        new SequenceEntityModifier(
                                new DelayModifier(2f, new IEntityModifier.IEntityModifierListener() {
                                    @Override
                                    public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                                    }

                                    @Override
                                    public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                                    }
                                }),
                                new AlphaModifier(0.3f, 1, 0, new IEntityModifier.IEntityModifierListener() {
                                    @Override
                                    public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                                    }

                                    @Override
                                    public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                                        MainBoardScreen.getInstanceEngine().runOnUpdateThread(new Runnable() {
                                            @Override
                                            public void run() {
                                                earthquakeText.detachSelf();
                                                TilesPool.pushEntityToStack(earthquakeText);
                                            }
                                        });
                                    }
                                }))
                )));


    }

    public static void startEarthquake(final Runnable onFinish) {
        GameSound.getCurrentInstance().playEarthquakeSound();
        final int[] crystalsRemain = {19};
        final int[] randomCrystalIndices = new int[20];
        for (int i = 0; i < 20; i++) {
            randomCrystalIndices[i] = i * 3 + MathUtils.random(1, 3);
        }
        final int[] nextCellIndex = {0};
        int directionRandom = MathUtils.random(0, 3);
        MainBoardScreen.getInstanceEngine().registerUpdateHandler(new TimerHandler(3f, new ITimerCallback() {
            @Override
            public void onTimePassed(TimerHandler pTimerHandler) {
                GameSound.getCurrentInstance().playCrystalMatchSound(0);
            }
        }));
        MainBoardScreen.getInstanceEngine().registerUpdateHandler(new TimerHandler(1.5f, new ITimerCallback() {
            @Override
            public void onTimePassed(TimerHandler pTimerHandler) {
                GameSound.getCurrentInstance().playCoinDropSound();
            }
        }));

        MainBoardScreen.getInstanceEngine().registerUpdateHandler(new TimerHandler(3f, new ITimerCallback() {
            @Override
            public void onTimePassed(TimerHandler pTimerHandler) {
                GameSound.getCurrentInstance().playCrystalMatchSound(0);
            }
        }));

        for (int row = 0; row < IConstants.CELLS_VERTICAL; row++) {
            for (int col = 0; col < IConstants.CELLS_HORIZONTAL; col++) {
                if (TileBoard.isCellValid(col, row)) {
                    {
                        final TileSprite tile = TileBoard.tileMap.get(TileBoard.getKey(row, col));
                        final int finalRow1 = row;
                        final int finalCol1 = col;
                        int direction = 0;
                        switch (directionRandom) {
                            case 0:
                                direction = col;
                                break;
                            case 1:
                                direction = IConstants.CELLS_HORIZONTAL - col - 1;
                                break;
                            case 2:
                                direction = row;
                                break;
                            case 3:
                                direction = IConstants.CELLS_VERTICAL - row - 1;
                                break;
                            default:
                                direction = col;
                        }
                        final float delay = 0.2f * direction;
                        tile.getTile().registerEntityModifier(new SequenceEntityModifier(
                                new DelayModifier(delay, new IEntityModifier.IEntityModifierListener() {
                                    @Override
                                    public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                                    }

                                    @Override
                                    public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                                        if (MathUtils.isOdd(finalCol1 + finalRow1) && MathUtils.random(0, 1) == 1) {
                                            ParticleUtil.effectDisappearWithRandomSmoke(TileSpriteUtil.getTileCenterX(tile.getTile().getX()),
                                                    TileSpriteUtil.getTileCenterY(tile.getTile().getY()));
                                        }
                                    }
                                }),
                                new MoveByModifier(0.2f, 0f, 20f),
                                new MoveByModifier(0.3f, 0f, -25f),
                                new MoveByModifier(0.1f, 0f, 5f, new IEntityModifier.IEntityModifierListener() {
                                    @Override
                                    public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                                        nextCellIndex[0]++;
                                        if (crystalsRemain[0] > 0 && (nextCellIndex[0] == randomCrystalIndices[20 - crystalsRemain[0] - 1])) {
                                            if (!(tile instanceof ManaIncreasingTile) && MathUtils.random(0, 2) > 0) {
                                                appearCrystalAndGoToMana(tile, delay);
                                                crystalsRemain[0]--;
                                            } else {
                                                appearCoinAndAddPoints(tile, delay);
                                                crystalsRemain[0]--;
                                            }
                                        }
                                    }

                                    @Override
                                    public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                                    }
                                }),
                                /*
                                new DelayModifier(0.1f * row + 0.1f * (10 - col), new IEntityModifier.IEntityModifierListener() {
                                    @Override
                                    public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                                    }

                                    @Override
                                    public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                                        if (MathUtils.isOdd(finalCol1 + finalRow1) && MathUtils.random(0, 1) == 1) {
                                            ParticleUtil.effectDisappearWithRandomSmoke(TileSpriteUtil.getTileCenterX(tile.getTile().getX()),
                                                    TileSpriteUtil.getTileCenterY(tile.getTile().getY()));
                                        }
                                    }
                                }),
                                new MoveByModifier(0.1f, 0f, 20f),
                                new MoveByModifier(0.3f, 0f, -20f),*/
                                new DelayModifier(0.5f)
                        ));
                    }
                }
            }
        }

        MainBoardScreen.getInstanceEngine().registerUpdateHandler(new TimerHandler(3f, new ITimerCallback() {
            @Override
            public void onTimePassed(TimerHandler pTimerHandler) {
                onFinish.run();
            }
        }));
    }

    private static void appearCoinAndAddPoints(final TileSprite tile, float delay) {
        final float fromX = TileSpriteUtil.getTileCenterX(tile.getTile().getX());
        final float fromY = TileSpriteUtil.getTileCenterY(tile.getTile().getY());


        final Sprite coin = TilesPool.getEntityForTexture(ResourceManager.getInstance().getGoldCoinParticleRegion());
        coin.setPosition(fromX, fromY);
        coin.setScale(0.7f);
        MainBoardScreen.getScm().getBonusLayer().attachChild(coin);
        coin.setAlpha(0f);
        coin.registerEntityModifier(new SequenceEntityModifier(
                new ParallelEntityModifier(
                        new AlphaModifier(0.3f, 0, 1),
                        new ScaleModifier(0.3f, 0.2f, 0.5f),
                        new MoveByModifier(0.3f, 0, 50)
                ),
                new DelayModifier(2.5f - delay, new IEntityModifier.IEntityModifierListener() {
                    @Override
                    public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                        //showStarRotationEffect(0.5f, 0.7f, fromX, fromY+50, MainBoardScreen.getScm().getBonusLayer());
                        ParticleUtil.makeParticleCircle(1.5f,
                                fromX, fromY + 50,
                                20,
                                0.2f,
                                2,
                                20,
                                ResourceManager.getInstance().getGoldParticleRegion()
                        );
                    }

                    @Override
                    public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                        MainBoardScreen.getInstanceEngine().runOnUpdateThread(new Runnable() {
                            @Override
                            public void run() {
                                coin.detachSelf();
                                TilesPool.pushEntityToStack(coin);
                            }
                        });
                        GameProfile.getCurrentLevelProfile().addAchievedPoints(500, 1,
                                tile.getTile().getX(),
                                tile.getTile().getY());
                    }
                })
        ));


    }

    private static void appearCrystalAndGoToMana(TileSprite tile, float delay) {
        final float fromX = TileSpriteUtil.getTileCenterX(tile.getTile().getX());
        final float fromY = TileSpriteUtil.getTileCenterY(tile.getTile().getY());
        ManaAndPointsController.getInstance().addMana(IConstants.manaUseForMatch);
        ManaAndPointsController.getInstance().updateMana();


        final Sprite crystal = TilesPool.getEntityForTexture(ResourceManager.getInstance().getMagicTileTextureRegion());
        crystal.setPosition(fromX, fromY);
        crystal.setScale(0.7f);
        MainBoardScreen.getScm().getBonusLayer().attachChild(crystal);
        crystal.setAlpha(0f);
        crystal.registerEntityModifier(new SequenceEntityModifier(
                new ParallelEntityModifier(
                        new AlphaModifier(0.3f, 0, 1),
                        new ScaleModifier(0.3f, 0.2f, 0.5f),
                        new MoveByModifier(0.3f, 0, 50)
                ),
                new DelayModifier(2.5f - delay, new IEntityModifier.IEntityModifierListener() {
                    @Override
                    public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
                        //showStarRotationEffect(0.5f, 0.7f, fromX, fromY+50, MainBoardScreen.getScm().getBonusLayer());
                        ParticleUtil.makeParticleCircle(1.5f,
                                fromX, fromY + 50,
                                20,
                                0.2f,
                                2,
                                20,
                                ResourceManager.getInstance().getGoldParticleRegion()
                        );
                    }

                    @Override
                    public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                        MainBoardScreen.getInstanceEngine().runOnUpdateThread(new Runnable() {
                            @Override
                            public void run() {
                                crystal.detachSelf();
                                TilesPool.pushEntityToStack(crystal);
                            }
                        });
                        ParticleUtil.makeCrystalPath(IConstants.CRYSTAL_COLLECT_DURATION, 0.7f, fromX, fromY + 50, ManaAndPointsController.MANA_TARGET_X, ManaAndPointsController.MANA_TARGET_Y);

                        ParticleUtil.makeParticlePath(fromX, fromY + 50, ManaAndPointsController.MANA_TARGET_X, ManaAndPointsController.MANA_TARGET_Y,
                                ResourceManager.getInstance().getParticleRegion());
                    }
                })
        ));
    }


}
